package com.googlecode.yoohoo.io.socket.stream;

import org.apache.mina.common.IoSession;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;

import com.googlecode.yoohoo.config.IConfigManager;
import com.googlecode.yoohoo.io.socket.AbstractConnectionContext;
import com.googlecode.yoohoo.io.socket.ConfigManager;
import com.googlecode.yoohoo.utils.AppUtils;
import com.googlecode.yoohoo.xmppcore.model.account.IAccountManager;
import com.googlecode.yoohoo.xmppcore.protocol.stream.error.InternalServerError;
import com.googlecode.yoohoo.xmppcore.stream.INegotiationContext;
import com.googlecode.yoohoo.xmppcore.stream.IStreamNegotiant;
import com.googlecode.yoohoo.xmppcore.utils.ErrorUtils;

public class NegotiationContext extends AbstractConnectionContext implements INegotiationContext {
	public static final String KEY_NEGOTIATION_DONE = "$$negotiation.done$$";
	
	private IStreamNegotiant negotiant;
	private boolean tlsRequired;
	private int authMaxRetries;
	private String appHome;
	private String hostName;
	
	public NegotiationContext(IoSession ioSession, BundleContext bundleContext) {
		super(ioSession, bundleContext);
		IConfigManager configManager = AppUtils.getConfigManager(bundleContext);
		tlsRequired = configManager.getBoolean(ConfigManager.KEY_TLS_REQUIRED);
		authMaxRetries = configManager.getInt(ConfigManager.KEY_AUTH_MAX_RETRIES);
		appHome = AppUtils.getAppHome(bundleContext);
		hostName = AppUtils.getHostName(bundleContext);
	}

	@Override
	public boolean isNegotiationDone() {
		return Boolean.TRUE == getAttribute(KEY_NEGOTIATION_DONE);
	}
	
	@Override
	public IStreamNegotiant getNegotiant() {
		if (negotiant == null) {
			IStreamFeaturesManager featuresManager = getFeaturesManager();
			AbstractStreamNegotiant initialStreamNegotiant = new InitialStreamNegotiant(featuresManager);
			
			AbstractStreamNegotiant afterInitialStreamNegotiant = new AfterInitialStreamNegotiant();
			initialStreamNegotiant.setNextNegotiant(afterInitialStreamNegotiant);
			
			AbstractStreamNegotiant tlsINegotiant = new TlsNegotiant(featuresManager, appHome, hostName, tlsRequired);
			afterInitialStreamNegotiant.setNextNegotiant(tlsINegotiant);
			
			AbstractStreamNegotiant afterTlsNegotiant = new AfterTlsNegotiant();
			tlsINegotiant.setNextNegotiant(afterTlsNegotiant);
			
			AbstractStreamNegotiant saslNegotiant = new SaslNegotiant(featuresManager, getAccountManager(),
					hostName, authMaxRetries);
			afterTlsNegotiant.setNextNegotiant(saslNegotiant);
			
			AbstractStreamNegotiant resourceBindingNegotiant = new ResourceBindingNegotiant(hostName);
			saslNegotiant.setNextNegotiant(resourceBindingNegotiant);
			
			AbstractStreamNegotiant sessionEstablishmentNegotiant = new SessionEstablishmentNegotiant();
			resourceBindingNegotiant.setNextNegotiant(sessionEstablishmentNegotiant);
			
			negotiant = initialStreamNegotiant;
		}
		
		return negotiant;
	}

	private IAccountManager getAccountManager() {
		ServiceReference reference = bundleContext.getServiceReference(
				IAccountManager.class.getName());
		if (reference == null)
			return null;
		
		return (IAccountManager)bundleContext.getService(reference);
	}

	private IStreamFeaturesManager getFeaturesManager() {
		ServiceReference reference = bundleContext.getServiceReference(
				IStreamFeaturesManager.class.getName());
		if (reference == null) {
			throw ErrorUtils.createProtocolException("Can't get features manager", new InternalServerError());
		}
		
		return (IStreamFeaturesManager)bundleContext.getService(reference);
	}

}
